'Project:Oversight' to 'Project:Suppress'.
* (T84937) Free external links ("autolinked" urls) will now be terminated
by and HTML entity encodings of  , <, and >.
+* DatabaseBase::resultObject() is now protected (use outside Database classes
+ not necessary since 1.11).
== Compatibility ==
*/
public function clearSharedCache() {
$id = $this->getId();
- if ( $id ) {
- $key = wfMemcKey( 'user', 'id', $id );
- ObjectCache::getMainWANInstance()->delete( $key );
+ if ( !$id ) {
+ return;
}
+
+ $key = wfMemcKey( 'user', 'id', $id );
+ wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle( function() use ( $key ) {
+ ObjectCache::getMainWANInstance()->delete( $key );
+ } );
}
/**
array( 'user_id' => $this->id ),
__METHOD__ );
- $cache = ObjectCache::getMainWANInstance();
$key = wfForeignMemcKey( $this->database, false, 'user', 'id', $this->id );
- $cache->delete( $key );
+ $this->db->onTransactionPreCommitOrIdle( function() use ( $key ) {
+ ObjectCache::getMainWANInstance()->delete( $key );
+ } );
}
}
return $this->__call( __FUNCTION__, func_get_args() );
}
- public function resultObject( $result ) {
- return $this->__call( __FUNCTION__, func_get_args() );
- }
-
public function ping() {
return $this->__call( __FUNCTION__, func_get_args() );
}
* Once upon a time, DatabaseBase::query() returned a bare MySQL result
* resource, and it was necessary to call this function to convert it to
* a wrapper. Nowadays, raw database objects are never exposed to external
- * callers, so this is unnecessary in external code. For compatibility with
- * old code, ResultWrapper objects are passed through unaltered.
+ * callers, so this is unnecessary in external code.
*
- * @param bool|ResultWrapper|resource $result
+ * @param bool|ResultWrapper|resource|object $result
* @return bool|ResultWrapper
*/
- public function resultObject( $result ) {
- if ( empty( $result ) ) {
+ protected function resultObject( $result ) {
+ if ( !$result ) {
return false;
} elseif ( $result instanceof ResultWrapper ) {
return $result;
* @param bool|MssqlResultWrapper|resource $result
* @return bool|MssqlResultWrapper
*/
- public function resultObject( $result ) {
- if ( empty( $result ) ) {
+ protected function resultObject( $result ) {
+ if ( !$result ) {
return false;
} elseif ( $result instanceof MssqlResultWrapper ) {
return $result;
*/
public function timestampOrNull( $ts = null );
- /**
- * Take the result from a query, and wrap it in a ResultWrapper if
- * necessary. Boolean values are passed through as is, to indicate success
- * of write queries or failure.
- *
- * Once upon a time, DatabaseBase::query() returned a bare MySQL result
- * resource, and it was necessary to call this function to convert it to
- * a wrapper. Nowadays, raw database objects are never exposed to external
- * callers, so this is unnecessary in external code. For compatibility with
- * old code, ResultWrapper objects are passed through unaltered.
- *
- * @param bool|ResultWrapper|resource $result
- * @return bool|ResultWrapper
- */
- public function resultObject( $result );
-
/**
* Ping the server and try to reconnect if it there is no connection
*
* @return void
*/
function invalidateImageRedirect( Title $title ) {
- $cache = ObjectCache::getMainWANInstance();
-
- $memcKey = $this->getSharedCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
- if ( $memcKey ) {
- // Set a temporary value for the cache key, to ensure
- // that this value stays purged long enough so that
- // it isn't refreshed with a stale value due to a
- // lagged slave.
- $cache->delete( $memcKey, 12 );
+ $key = $this->getSharedCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
+ if ( $key ) {
+ $this->getMasterDB()->onTransactionPreCommitOrIdle( function() use ( $key ) {
+ ObjectCache::getMainWANInstance()->delete( $key );
+ } );
}
}
return;
}
- ObjectCache::getMainWANInstance()->delete( $key );
+ $this->repo->getMasterDB()->onTransactionPreCommitOrIdle( function() use ( $key ) {
+ ObjectCache::getMainWANInstance()->delete( $key );
+ } );
}
/**
return str_replace( array( '._', '_.' ), '.', $key );
}
- public function produceStatsdData( $key, $value = 1, $metric = StatsdDataInterface::STATSD_METRIC_COUNT ) {
+ public function produceStatsdData(
+ $key, $value = 1, $metric = StatsdDataInterface::STATSD_METRIC_COUNT
+ ) {
$entity = $this->produceStatsdDataEntity();
if ( $key !== null ) {
$key = self::normalizeMetricKey( "{$this->prefix}.{$key}" );
// Check for global @embed comment and remove it. Allow other comments to be present
// before @embed (they have been replaced with placeholders at this point).
$embedAll = false;
- $rule = preg_replace( '/^((?:\s+|' . CSSMin::PLACEHOLDER . '(\d+)x)*)' . CSSMin::EMBED_REGEX . '\s*/', '$1', $rule, 1, $embedAll );
+ $rule = preg_replace(
+ '/^((?:\s+|' .
+ CSSMin::PLACEHOLDER .
+ '(\d+)x)*)' .
+ CSSMin::EMBED_REGEX .
+ '\s*/',
+ '$1',
+ $rule,
+ 1,
+ $embedAll
+ );
// Build two versions of current rule: with remapped URLs
// and with embedded data: URIs (where possible).
* - a) Replication lag is bounded to being less than HOLDOFF_TTL; or
* - b) If lag is higher, the DB will have gone into read-only mode already
*
+ * When using potentially long-running ACID transactions, a good pattern is
+ * to use a pre-commit hook to issue the delete. This means that immediately
+ * after commit, callers will see the tombstone in cache in the local datacenter
+ * and in the others upon relay. It also avoids the following race condition:
+ * - a) T1 begins, changes a row, and calls delete()
+ * - b) The HOLDOFF_TTL passes, expiring the delete() tombstone
+ * - c) T2 starts, reads the row and calls set() due to a cache miss
+ * - d) T1 finally commits
+ * - e) Stale value is stuck in cache
+ *
+ * Example usage:
+ * @code
+ * $dbw->begin(); // start of request
+ * ... <execute some stuff> ...
+ * // Update the row in the DB
+ * $dbw->update( ... );
+ * $key = wfMemcKey( 'homes', $homeId );
+ * // Purge the corresponding cache entry just before committing
+ * $dbw->onTransactionPreCommitOrIdle( function() use ( $cache, $key ) {
+ * $cache->delete( $key );
+ * } );
+ * ... <execute some stuff> ...
+ * $dbw->commit(); // end of request
+ * @endcode
+ *
* If called twice on the same key, then the last hold-off TTL takes
* precedence. For idempotence, the $ttl should not vary for different
* delete() calls on the same key. Also note that lowering $ttl reduces
*/
public $mUsedOptions;
- public $mVersion = Parser::VERSION, # Compatibility check
- $mCacheTime = '', # Time when this object was generated, or -1 for uncacheable. Used in ParserCache.
- $mCacheExpiry = null, # Seconds after which the object should expire, use 0 for uncacheable. Used in ParserCache.
- $mCacheRevisionId = null; # Revision ID that was parsed
+ # Compatibility check
+ public $mVersion = Parser::VERSION;
+
+ # Time when this object was generated, or -1 for uncacheable. Used in ParserCache.
+ public $mCacheTime = '';
+
+ # Seconds after which the object should expire, use 0 for uncacheable. Used in ParserCache.
+ public $mCacheExpiry = null;
+
+ # Revision ID that was parsed
+ public $mCacheRevisionId = null;
/**
* @return string TS_MW timestamp
$parser->setFunctionHook( $func, array( __CLASS__, $func ), Parser::SFH_NO_HASH );
}
- $parser->setFunctionHook( 'namespace', array( __CLASS__, 'mwnamespace' ), Parser::SFH_NO_HASH );
+ $parser->setFunctionHook(
+ 'namespace',
+ array( __CLASS__, 'mwnamespace' ),
+ Parser::SFH_NO_HASH
+ );
$parser->setFunctionHook( 'int', array( __CLASS__, 'intFunction' ), Parser::SFH_NO_HASH );
$parser->setFunctionHook( 'special', array( __CLASS__, 'special' ) );
$parser->setFunctionHook( 'speciale', array( __CLASS__, 'speciale' ) );
$parser->setFunctionHook( 'formatdate', array( __CLASS__, 'formatDate' ) );
if ( $wgAllowDisplayTitle ) {
- $parser->setFunctionHook( 'displaytitle', array( __CLASS__, 'displaytitle' ), Parser::SFH_NO_HASH );
+ $parser->setFunctionHook(
+ 'displaytitle',
+ array( __CLASS__, 'displaytitle' ),
+ Parser::SFH_NO_HASH
+ );
}
if ( $wgAllowSlowParserFunctions ) {
$parser->setFunctionHook(
* @param int $direction
* @return string
*/
- public static function pad( $parser, $string, $length, $padding = '0', $direction = STR_PAD_RIGHT ) {
+ public static function pad(
+ $parser, $string, $length, $padding = '0', $direction = STR_PAD_RIGHT
+ ) {
$padding = $parser->killMarkers( $padding );
$lengthOfPadding = mb_strlen( $padding );
if ( $lengthOfPadding == 0 ) {
}
$limitReport .= 'Cached time: ' . $this->mOutput->getCacheTime() . "\n";
$limitReport .= 'Cache expiry: ' . $this->mOutput->getCacheExpiry() . "\n";
- $limitReport .= 'Dynamic content: ' . ( $this->mOutput->hasDynamicContent() ? 'true' : 'false' ) . "\n";
+ $limitReport .= 'Dynamic content: ' .
+ ( $this->mOutput->hasDynamicContent() ? 'true' : 'false' ) .
+ "\n";
+
foreach ( $this->mOutput->getLimitReportData() as $key => $value ) {
if ( Hooks::run( 'ParserLimitReportFormat',
array( $key, &$value, &$limitReport, false, false )
* @private
*/
public function makeFreeExternalLink( $url, $numPostProto ) {
-
$trail = '';
# The characters '<' and '>' (which were escaped by
# URLs, per RFC 2396.
# Make terminate a URL as well (bug T84937)
$m2 = array();
- if ( preg_match( '/&(lt|gt|nbsp|#x0*(3[CcEe]|[Aa]0)|#0*(60|62|160));/', $url, $m2, PREG_OFFSET_CAPTURE ) ) {
+ if ( preg_match(
+ '/&(lt|gt|nbsp|#x0*(3[CcEe]|[Aa]0)|#0*(60|62|160));/',
+ $url,
+ $m2,
+ PREG_OFFSET_CAPTURE
+ ) ) {
$trail = substr( $url, $m2[0][1] ) . $trail;
$url = substr( $url, 0, $m2[0][1] );
}
wfIncrStats( "pcache.miss.revid" );
$revId = $article->getLatest();
$cachedRevId = $value->getCacheRevisionId();
- wfDebug( "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId\n" );
+ wfDebug(
+ "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId\n"
+ );
$value = false;
- } elseif ( Hooks::run( 'RejectParserCacheValue', array( $value, $wikiPage, $popts ) ) === false ) {
+ } elseif (
+ Hooks::run( 'RejectParserCacheValue', array( $value, $wikiPage, $popts ) ) === false
+ ) {
wfIncrStats( 'pcache.miss.rejected' );
- wfDebug( "ParserOutput key valid, but rejected by RejectParserCacheValue hook handler.\n" );
+ wfDebug(
+ "ParserOutput key valid, but rejected by RejectParserCacheValue hook handler.\n"
+ );
$value = false;
} else {
wfIncrStats( "pcache.hit" );
// ...and its pointer
$this->mMemc->set( $this->getOptionsKey( $page ), $optionsKey, $expire );
- Hooks::run( 'ParserCacheSaveComplete', array( $this, $parserOutput, $page->getTitle(), $popts, $revId ) );
+ Hooks::run(
+ 'ParserCacheSaveComplete',
+ array( $this, $parserOutput, $page->getTitle(), $popts, $revId )
+ );
} else {
wfDebug( "Parser output was marked as uncacheable and has not been saved.\n" );
}
}
/**
- * Sets a hook to force that a page exists, and sets a current revision callback to return a
- * revision with custom content when the current revision of the page is requested.
+ * Sets a hook to force that a page exists, and sets a current revision callback to return
+ * a revision with custom content when the current revision of the page is requested.
*
* @since 1.25
* @param Title $title
* @return ScopedCallback to unset the hook
*/
public function setupFakeRevision( $title, $content, $user ) {
- $oldCallback = $this->setCurrentRevisionCallback( function ( $titleToCheck, $parser = false ) use ( $title, $content, $user, &$oldCallback ) {
- if ( $titleToCheck->equals( $title ) ) {
- return new Revision( array(
- 'page' => $title->getArticleID(),
- 'user_text' => $user->getName(),
- 'user' => $user->getId(),
- 'parent_id' => $title->getLatestRevId(),
- 'title' => $title,
- 'content' => $content
- ) );
- } else {
- return call_user_func( $oldCallback, $titleToCheck, $parser );
+ $oldCallback = $this->setCurrentRevisionCallback(
+ function (
+ $titleToCheck, $parser = false ) use ( $title, $content, $user, &$oldCallback
+ ) {
+ if ( $titleToCheck->equals( $title ) ) {
+ return new Revision( array(
+ 'page' => $title->getArticleID(),
+ 'user_text' => $user->getName(),
+ 'user' => $user->getId(),
+ 'parent_id' => $title->getLatestRevId(),
+ 'title' => $title,
+ 'content' => $content
+ ) );
+ } else {
+ return call_user_func( $oldCallback, $titleToCheck, $parser );
+ }
}
- } );
+ );
+
global $wgHooks;
$wgHooks['TitleExists'][] =
function ( $titleToCheck, &$exists ) use ( $title ) {
} else {
$options['ORDER BY'] = 'qc_value ASC';
}
- $res = $dbr->select( 'querycache', array( 'qc_type',
+ return $dbr->select( 'querycache', array( 'qc_type',
'namespace' => 'qc_namespace',
'title' => 'qc_title',
'value' => 'qc_value' ),
array( 'qc_type' => $this->getName() ),
__METHOD__, $options
);
- return $dbr->resultObject( $res );
}
public function getCachedTimestamp() {
'name' => $submitVar,
'value' => $movepagebtn,
'label' => $movepagebtn,
- 'flags' => array( 'progressive', 'primary' ),
+ 'flags' => array( 'constructive', 'primary' ),
'type' => 'submit',
) ),
array(
* @covers Linker::formatAutocomments
* @covers Linker::formatLinksInComment
*/
- public function testFormatComment( $expected, $comment, $title = false, $local = false, $wikiId = null ) {
+ public function testFormatComment(
+ $expected, $comment, $title = false, $local = false, $wikiId = null
+ ) {
$conf = new SiteConfiguration();
$conf->settings = array(
'wgServer' => array(